//
// Created by Amon on 2021/10/16.
//

#include <unistd.h>
#include <unwind.h>
#include "CrashAnalyser.h"

pthread_cond_t signal_cond;
pthread_cond_t exception_cond;
pthread_mutex_t signal_mutex;
pthread_mutex_t exception_mutex;

native_handler_context *handler_context;

_Unwind_Reason_Code unwind_callback(struct _Unwind_Context *context, void *arg) {
    auto *const s = static_cast<native_handler_context *const>(arg);
    const uintptr_t pc = _Unwind_GetIP(context);
    if (pc != 0x0) {
        LOGE("pc:%p", pc);
        s->frames[s->frame_size++] = pc;
    }
    if (s->frame_size == BACKTRACK_FRAMES_MAX) {
        return _URC_END_OF_STACK;
    }
    return _URC_NO_REASON;
}

bool should_exit;
bool is_crash_catch = false;
int status;

void copy_info_context(int num, siginfo *pSi, void *pSc);

void analysis_native_exception() {
    const char *posix_desc = desc_sig(handler_context->si->si_signo, handler_context->si->si_code);
    LOGE("posix_desc:%s, signal:%d, address:%p",
         posix_desc, handler_context->si->si_signo, handler_context->si->si_addr);
    LOGE("process_name:%s, thread_name:%s",
         handler_context->process_name, handler_context->thread_name);
}

void wait_for_signal() {
    pthread_mutex_lock(&signal_mutex);
    LOGE("wait_for_signal start");
    pthread_cond_wait(&signal_cond, &signal_mutex);
    pthread_mutex_unlock(&signal_mutex);
    LOGE("wait_for_signal end");
}

void init_condition() {
    handler_context = static_cast<native_handler_context *>(malloc(sizeof(native_handler_context)));
    pthread_cond_init(&signal_cond, nullptr);
    pthread_mutex_init(&signal_mutex, nullptr);
    pthread_cond_init(&exception_cond, nullptr);
    pthread_mutex_init(&exception_mutex, nullptr);
}

void *thread_crash_monitor(void *argv) {
    auto *jniBridge = static_cast<JNIBridge *>(argv);
    should_exit = false;
    while (!should_exit) {
        wait_for_signal();
        analysis_native_exception();
        jniBridge->throwException2Java(handler_context);
    }
    status = 1;
    return &status;
}

void notify_thread_exception(int signal_num, struct siginfo *si, void *sc) {
    copy_info_context(signal_num, si, sc);
    pthread_mutex_lock(&signal_mutex);
    pthread_cond_signal(&signal_cond);
    pthread_mutex_unlock(&signal_mutex);
}

void copy_info_context(int num, siginfo *pSi, void *pSc) {
    handler_context->code = num;
    handler_context->si = pSi;
    handler_context->sc = pSc;
    handler_context->pid = getpid();
    handler_context->tid = gettid();
    handler_context->process_name = get_process_name(handler_context->pid);
    if (handler_context->pid == handler_context->tid) {
        handler_context->thread_name = "main";
    } else {
        handler_context->thread_name = get_thread_name(handler_context->tid);
    }
    handler_context->frame_size = 0;
    _Unwind_Backtrace(unwind_callback, handler_context);
}
